package com.gitee.alpha.ie;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.EnumUtil;
import cn.hutool.core.util.ReflectUtil;
import cn.hutool.poi.excel.WorkbookUtil;
import com.alibaba.excel.annotation.ExcelIgnore;
import com.alibaba.excel.converters.Converter;
import com.gitee.alpha.framework.repository.support.ColumnType;
import com.gitee.alpha.ie.convert.ConverterRegister;
import com.gitee.alpha.ie.vo.CommentVo;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import java.io.File;
import java.lang.reflect.Field;
import java.util.Comparator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.ss.usermodel.Workbook;

/**
 * excel util (import/export)<br>
 *
 * @author 冰羽
 * @version 2.2.0
 */
@Slf4j
public abstract class AbstractExcelHelper {
  private static Map<String, List<ExcelField>> _HEADER_ = new ConcurrentHashMap<>();

  /**
   * 构建表头
   *
   * @param entityClass 实体类型
   * @param ignore 忽略表头列表
   * @return
   */
  public List<List<String>> buildHead(Class<?> entityClass, String... ignore) {
    List<ExcelField> excelFields = buildExcelFields(entityClass);
    return buildHead(excelFields, Sets.newHashSet(ignore));
  }

  /**
   * 构建表头
   *
   * @param entityClass 实体类型
   * @param ignore 忽略表头列表
   * @return
   */
  public List<List<String>> buildHead(Class<?> entityClass, Set<String> ignore) {
    List<ExcelField> excelFields = buildExcelFields(entityClass);
    return buildHead(excelFields, ignore);
  }

  /**
   * 获取单元格上面的常量值
   *
   * @param excelFields 字段列表
   * @return
   */
  public Map<Integer, String[]> buildDropDownValues(List<ExcelField> excelFields) {
    Map<Integer, String[]> dropDownValues = Maps.newHashMap();

    if (CollectionUtil.isNotEmpty(excelFields)) {
      excelFields.forEach(
          excelField -> {
            if (excelField != null && CollectionUtil.isNotEmpty(excelField.getDropDownValues())) {
              dropDownValues.putAll(excelField.getDropDownValues());
            }
          });
    }

    return dropDownValues;
  }

  public List<CommentVo> buildCommentVos(List<ExcelField> excelFields, int rowIndex) {
    return buildCommentVos(excelFields, rowIndex, 1, 1);
  }

  public int getSheetCounts(String name) throws Exception {
    return getSheetCounts(new File(name));
  }

  public int getSheetCounts(File file) throws Exception {
    Workbook workbook = WorkbookUtil.createBook(file);
    int sheetNumbers = workbook.getNumberOfSheets();
    // 需要关闭流，否在文件一直处于打开状态
    workbook.close();

    return sheetNumbers;
  }

  /**
   * 获取单元格注释
   *
   * @param excelFields 字段列表
   * @param rowIndex 行
   * @param rowOffset 行偏移
   * @param colOffset 列偏移
   * @return
   */
  public abstract List<CommentVo> buildCommentVos(
      List<ExcelField> excelFields, int rowIndex, int rowOffset, int colOffset);

  /**
   * 构建ExcelField {@link ExcelField}
   *
   * @param entityClass 实体类型
   * @return
   */
  public List<ExcelField> buildExcelFields(Class<?> entityClass) {
    Assert.isFalse(entityClass == null, "IEntity类类型不能为空");
    String key = entityClass.getName();
    List<ExcelField> excelFields = _HEADER_.get(key);

    if (CollectionUtil.isNotEmpty(excelFields)) {
      return excelFields;
    }

    synchronized (entityClass) {
      // 获取字段列表
      List<Field> fields = findFields(entityClass);
      Assert.isFalse(
          CollectionUtil.isEmpty(fields),
          String.format(
              "没有获取需要导出到Excel的Entity的Head, 请检查或字类重写%s的findFields方法",
              AbstractExcelHelper.class.getName()));
      excelFields = findFields(fields);
      // 排序
      excelFields = sort(excelFields);
      // 设置实体类型(数据导入使用该类型newInstance)
      excelFields.forEach(excelField -> excelField.setEntityType(entityClass));

      // 写入内部缓存
      _HEADER_.put(key, excelFields);

      return excelFields;
    }
  }

  /**
   * 排序
   *
   * @param excelFields 字段列表
   * @return
   */
  protected List<ExcelField> sort(List<ExcelField> excelFields) {
    // 默认按照order顺序排序
    return CollectionUtil.sort(excelFields, Comparator.comparingInt(ExcelField::getCellIndex));
  }

  protected List<Field> findFields(Class<?> entityClass) {
    List<Field> fields = Lists.newArrayList();
    Field[] declaredFields = ReflectUtil.getFields(entityClass);
    for (Field field : declaredFields) {
      ExcelIgnore excelIgnore = field.getAnnotation(ExcelIgnore.class);
      if (excelIgnore == null) {
        fields.add(field);
      }
    }

    return fields;
  }

  public List<Converter> findConverters(List<ExcelField> excelFields) {
    Set<Converter> converters = Sets.newHashSet();

    if (CollectionUtil.isNotEmpty(excelFields)) {
      excelFields.forEach(
          item -> {
            Converter converter = item.getConverter();
            if (converter != null) {
              converters.add(converter);
            }
          });
    }

    return Lists.newArrayList(converters);
  }

  /**
   * 获取表头
   *
   * @param fields 实体字段列表
   * @return
   */
  protected abstract List<ExcelField> findFields(List<Field> fields);

  protected Map<Integer, String[]> buildDropDownValues(int colIndex, Field field) {
    Map<Integer, String[]> dropDownValues = Maps.newHashMap();
    Class clazz = field.getType();
    if (clazz.isEnum()) {
      LinkedHashMap<String, Object> enums = EnumUtil.getEnumMap(clazz);
      if (CollectionUtil.isNotEmpty(enums)) {
        List<String> list = Lists.newArrayList();

        enums.forEach((k, v) -> list.add(k));

        String[] values = ArrayUtil.toArray(list, String.class);
        if (ArrayUtil.isNotEmpty(values)) {
          dropDownValues.put(colIndex, values);
        }
      }
    } else if (ColumnType.BOOLEAN.getBasicType().equals(clazz.getName())
        || ColumnType.BOOLEAN.getJavaClass().getName().equals(clazz.getName())) {
      dropDownValues.put(colIndex, new String[] {"TRUE", "FALSE"});
    }

    return dropDownValues;
  }

  protected Converter getConverter(Class<?> entityClass) {
    return ConverterRegister.getConverter(entityClass);
  }

  protected List<List<String>> buildHead(List<ExcelField> excelFields, Set<String> ignore) {
    List<List<String>> head = Lists.newArrayList();

    excelFields.forEach(
        excelField -> {
          Field original = excelField.getOriginal();

          if (original != null && ignore != null && ignore.contains(original.getName())) {

          } else {
            List<String> list = Lists.newArrayList();
            if (excelField.isRequired()) {
              // 必填的需要加 * 标识
              list.add(String.format("%s*", excelField.getTitle()));
            } else {
              list.add(excelField.getTitle());
            }
            head.add(list);
          }
        });

    return head;
  }
}
